/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package taller1;

import Clases.Event;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;

/**
 *
 * @author jhtobav
 */
public class Punto1 {

    /* Variables */
    
    static int num_customers;
    static int num_customers_lost;
    static int num_max_customers_in_queue;
    static int next_customer_number;
    static double num_gallons_sold;
    static double num_gallons_lost;
    static double sim_time;
    static boolean[] servers = new boolean[2]; // false -> desocupado. true -> ocupado.
    static ArrayList<Double> arrival_times;
    static Event next_event;
    static LinkedList<Event> events = new LinkedList<>();

    /* Rutina Principal */
    public void main_function() {

        /* Inicializando la simulación */
        initialize();

        /* Simulación */

        while (next_customer_number < arrival_times.size()) {

            /* Determinando el siguiente tipo de evento y actualizando reloj*/
            timing();

            /* Invocar la función apropiada para el tipo de evento */
            switch (next_event.getEvent_type()) {
                case 1:
                    arrival();
                    break;
                case 2:
                    departure();
                    break;
            }

        }
        
        report();

    }

    /* función de inicialización */
    public static void initialize() {

        /* Inicializando el reloj de la simulación */
        sim_time = 0;

        /* Inicializando las variables de estado */
        servers[0] = false;
        servers[1] = false;
        num_max_customers_in_queue = 0;
        next_customer_number = 0;

        /* Inicializando las variables estadísticas */
        num_customers = 0;
        num_customers_lost = 0;
        num_gallons_sold = 0;
        num_gallons_lost = 0;
        
        /* Inicializando los tiempos de arribo de los clientes */
        arrival_times = all_arrival_times();

        /* Programando primer arribo */
        Event first_arrival = new Event();

        double arrival_time = time_next_arrival();

        first_arrival.setEvent_time(arrival_time);
        first_arrival.setEvent_type(1);

        events.add(first_arrival);

    }

    public static void timing() {

        /* Revisando si la lista de eventos esta vacía */
        if (events.size() == 0) {

            /* Lista de eventos vacía, Deteniendo la simulación */
            System.out.println("Lista de eventos vacía al minuto" + sim_time);
            return;

        }

        double min_time_next_event = 1.0e+29;

        /* Determinando el tipo del siguiente evento a ocurrir */
        for (Event event : events) {
            if (event.getEvent_time() < min_time_next_event) {
                min_time_next_event = event.getEvent_time();
                next_event = event;
            }
        }

        /* Aún quedan eventos, avanzar el reloj de la simulación */
        sim_time = min_time_next_event;

    }

    public static void arrival() {

        /* Revisar si los servidores estan ocupados */
        if (servers[0] && servers[1]) {

            /* Ambos servidores ocupados, se pierce el cliente */
            num_customers_lost ++;
            num_gallons_lost = num_gallons_lost + purchased_gallons();
            

        } else {

            /* Programar tiempo de demora en el servidor */
            Event next_departure = new Event();

            double departure_time = sim_time + delay_time();

            next_departure.setEvent_time(departure_time);
            next_departure.setEvent_type(2);

            if (!servers[0]) {

                servers[0] = true;

                /* Relacionar el evento con el servidor */
                next_departure.setEvent_server(0);

            } else {

                servers[1] = true;

                /* Relacionar el evento con el servidor */
                next_departure.setEvent_server(1);

            }

            events.add(next_departure);

            num_gallons_sold = num_gallons_sold + purchased_gallons();
            num_customers++;

        }

        /* Evento de arribo finalizado, removerlo de la lista de eventos*/
        events.remove(next_event);

        /* Programar siguiente llegada */
        Event next_arrival = new Event();

        double next_arrival_time = time_next_arrival();

        next_arrival.setEvent_time(next_arrival_time);
        next_arrival.setEvent_type(1);

        events.add(next_arrival);

    }

    public static void departure() {

        /* Liberal el servidor, no hay clientes esperando en la cola */
        servers[next_event.getEvent_server()] = false;

        /* Eliminando el eveto de salida */
        events.remove(next_event);

    }

    public static double delay_time() {

        return -5 * Math.log(getUniform());
    }

    public static double purchased_gallons() {

        return 3 + (7-3) * getUniform();
                
    }
    
    public static void report() {
        
        System.out.println("-------------------- Punto 1 ");
        System.out.println("Galones no vendidos diarios -> " + num_gallons_lost);
        System.out.println("Perdida diaria del dueño de la estación -> " + num_gallons_lost*8.2);
        System.out.println("Numero de clientes totales -> " + arrival_times.size());
        System.out.println("Numero de clientes atendidos - > " + num_customers);
        System.out.println("Numero de clientes perdidos -> " + num_customers_lost);
        System.out.println("Porcentaje clientes perdidos -> " + (num_customers_lost*100)/num_customers);
        
    }

    public static double time_next_arrival() {

        next_customer_number ++;
        return arrival_times.get(next_customer_number - 1);
        
    }
    
    public static ArrayList<Double> all_arrival_times() {

        /* Calculando el nonHomogeneus Poisson */
        int Tmax = 1440;

        double lamda = 0.5;
        ArrayList<Double> S = new ArrayList<>();
        Double t = 0.0;
        while (t < Tmax) {
            Double U1 = getUniform();
            Double U2 = getUniform();
            t = t - (1.0 / lamda) * Math.log(U1);
            if (U2 < (lambda(t) / lamda)) {
                S.add(t);
            }
        }

        return S;
    }
    
    public static double lambda(double t){

        if(t >= 0.0 && t<= 420.0)
            return 2.0/60.0;
        if(t >= 420.0 && t<= 1020.0)
            return (((7.0/9000.0)*t)-(22.0/75.0));
        if(t >= 1020.0 && t<= 1320.0)
            return 30.0/60.0;
        if(t >= 1320.0 && t<= 1440.0)
            return ((-7.0/1800.0)*t)+(169.0/30.0);
        return 123456789.0;
        
    }
    
    public static double getUniform(){
        
        Random b = new Random();
        return b.nextDouble();  
        
    }

}
